import { version } from "../package.json";
import { hasOwn } from "./tools";
import Views from "./views";
import BaseView from "./baseView";
import Calc from "./calc";
let ruleCalc = null;
let $views = Symbol("$views");
let path = Symbol("path");
let $vm = Symbol("$vm");
let getList = Symbol("getList");
let ergodicViews = Symbol("ergodicViews");
let getView = Symbol("getView");
let defOption = Object.create(null);
let blockEnter = false;
let direction = "";
let keyMaps = {
  LEFT: [3, 37],
  UP: [1, 38, 61],
  RIGHT: [4, 39],
  DOWN: [2, 40, 77],
  ENTER: [13],
  BACK: [8, 22, 27, 340, 461, 10009],
  NUMBER: [
    48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 96, 97, 98, 99, 100, 101, 102, 103,
    104, 105,
  ],
  FOWARD: [70, 417],
  REWIND: [82, 412],
  PLAY: [80, 415],
  PAUSE: [81, 19],
  STOP: [83, 413],
  DELETE: [46, 67],
};

/**
 * 默认按键向左方法
 */
function keyLeft() {
  typeof this.keyLeft === "function" ? this.keyLeft() : this.moveFocus("left");
}

/**
 * 默认按键向右方法
 */
function keyRight() {
  typeof this.keyRight === "function"
    ? this.keyRight()
    : this.moveFocus("right");
}

/**
 * 默认按键向上方法
 */
function keyUp() {
  typeof this.keyUp === "function" ? this.keyUp() : this.moveFocus("up");
}

/**
 * 默认按键向下方法
 */
function keyDown() {
  typeof this.keyDown === "function" ? this.keyDown() : this.moveFocus("down");
}

/**
 * 默认按键返回方法
 */
function keyBack() {
  let func =
    this.$options.back === "function"
      ? this.$options.back
      : () => {
          history.back();
        };
  typeof this.keyBack === "function" ? this.keyBack() : func();
}

/**
 * 检测当前响应按键名称
 * @param {Number} keyCode 按键值
 */
function checkKeycode(keyCode) {
  let keyAction = null;
  for (let actions in keyMaps) {
    if (keyMaps[actions].indexOf(keyCode) >= 0) {
      keyAction = actions;
      break;
    }
  }
  direction = keyAction && keyAction.toLocaleLowerCase();
  return keyAction;
}

/**
 * 按键响应方法
 * @param {String} keyAction 按键响应键名（处理后）
 */
function eventHandle(keyAction) {
  // 添加确定键响应阻断条件
  if (keyAction === "ENTER") {
    if (blockEnter) {
      return false;
    } else {
      blockEnter = true;
    }
  }
  switch (keyAction) {
    case "LEFT":
      keyLeft.call(this);
      break;
    case "UP":
      keyUp.call(this);
      break;
    case "RIGHT":
      keyRight.call(this);
      break;
    case "DOWN":
      keyDown.call(this);
      break;
    case "ENTER":
      this.keyEnter && this.keyEnter();
      break;
    case "BACK":
      keyBack.call(this);
      break;
    case "NUMBER":
      this.keyNumber && this.keyNumber();
      break;
    case "FOWARD":
      this.keyFoward && this.keyFoward();
      break;
    case "REWIND":
      this.keyRewind && this.keyRewind();
      break;
    case "PLAY":
      this.keyPlay && this.keyPlay();
      break;
    case "PAUSE":
      this.keyPause && this.keyPause();
      break;
    case "STOP":
      this.keyStop && this.keyStop();
      break;
    case "DELETE":
      this.keyDelete && this.keyDelete();
      break;
    default:
      break;
  }
}

/**
 * 按键方法还原
 */
function resetKey() {
  blockEnter = false;
  this.keyLeft = null;
  this.keyUp = null;
  this.keyRight = null;
  this.keyDown = null;
  this.keyEnter = null;
  this.keyBack = null;
  this.keyNumber = null;
  this.keyFoward = null;
  this.keyRewind = null;
  this.keyPlay = null;
  this.keyPause = null;
  this.keyStop = null;
  this.keyDelete = null;
}

export default class Service {
  constructor(option = {}, vm) {
    this.monitor = false;
    this[$vm] = vm;
    defOption = Object.assign(
      {
        limitTime: 200,
        selectName: "curselect",
        focusName: "focushover",
        failoverDist: 50,
        includeAttr: "",
        back() {
          history.back();
        },
      },
      option
    );
    ruleCalc = new Calc(defOption.failoverDist, keyMaps);
    let mapObj = option.keyMaps;
    if (mapObj) {
      ruleCalc.cnkiMaps(defOption.keyMaps);
    }
    this[$views] = new Views(); // 生成默认视图
    this[path] = []; // 当前路由匹配的列表
    this.keyLeft = null;
    this.keyUp = null;
    this.keyRight = null;
    this.keyDown = null;
    this.keyEnter = null;
    this.keyBack = null;
    this.keyNumber = null;
    this.keyFoward = null;
    this.keyRewind = null;
    this.keyPlay = null;
    this.keyPause = null;
    this.keyStop = null;
    this.keyDelete = null;
    (() => {
      this.setkeyHandle();
      // 默认按键响应间隔200毫秒
      let limitTime = this.$options.limitTime ? this.$options.limitTime : 0;
      let startTime = new Date().getTime();
      let eventRespond = (event) => {
        let keyCode = event.which ? event.which : event.keyCode;
        // 组织默认事件兼容处理
        if (typeof event.cancelable !== "boolean" || event.cancelable) {
          event.preventDefault();
        } else {
          console.log("The following event couldn't be canceled:");
        }
        // 阻止事件冒泡
        if (event.stopPropagation) {
          event.stopPropagation();
        } else if (typeof window !== "undefined") {
          window.event.cancelBubble = true;
        }
        let currentTime = new Date().getTime();
        let keyAction = checkKeycode.call(this, keyCode);
        let difference = currentTime - startTime;
        // 使用节流的形式限制按键响应
        if (this.monitor && currentTime - startTime > limitTime) {
          if (!this.pointer) {
            this.checkPosition(0, 0);
            return false;
          }
          if (blockEnter && difference > 500) {
            blockEnter = false;
          }
          startTime = currentTime;
          eventHandle.call(this, keyAction);
        }
      };

      // 事件监听
      if (typeof document !== "undefined" && document.addEventListener) {
        document.addEventListener("keydown", eventRespond, true);
      } else if (typeof document !== "undefined" && document.attachEvent) {
        document.attachEvent("onkeydown", eventRespond);
      }
    })();
  }

  /**
   * 版本号
   */
  get version() {
    return version;
  }

  /**
   * 配置选项
   */
  get $options() {
    return defOption;
  }

  /**
   * 当前焦点
   */
  get pointer() {
    return this.getPointer(this.popSign);
  }

  /**
   * 当前按键方向
   */
  get direction() {
    return direction;
  }

  /**
   * 当前弹窗标记名称
   */
  get popSign() {
    let popup = "";
    this[ergodicViews]((value) => {
      popup = value.popSign;
      return value.popSign;
    });
    return popup;
  }

  /**
   * 重置按键响应事件
   */
  setkeyHandle() {
    resetKey.call(this);
  }

  /**
   * 打开新的弹层
   * @param {String} popup 弹层标记
   * @param {Function} fn 焦点初始化方法，默认焦点放到第一个焦点
   */
  openPop(popup, fn) {
    if (popup) {
      let flag = true;
      this[ergodicViews]((value) => {
        value.popSign = "";
        if (flag && hasOwn(value, `popup_${popup}`)) {
          value.popSign = popup;
          flag = false;
        }
      });
      typeof fn === "function" ? fn() : !this.pointer && this.initFocus(0, 0);
    }
  }

  /**
   * 关闭弹窗
   */
  closePop() {
    this[ergodicViews]((value) => {
      value.popSign = "";
    });
    !this.pointer && this.checkPosition(0, 0);
  }

  /**
   * 元素添加去除焦点类名,并触发对应的获焦、失焦方法
   * @param {Object} pointer 需要添加焦点的焦点对象
   */
  checkFocus(next) {
    let prev = this.pointer;
    if (next && next !== prev) {
      if (prev) {
        ruleCalc.removClass(prev, this.$options.focusName);
        prev.__pools__.setPointer();
        if (typeof prev.vBlur === "function") {
          prev.vBlur(prev);
        }
      }
      if (next.sign) {
        if (prev && next.sign === prev.sign) {
          next.__sign__.forEach((value) => {
            value.selected = value === next;
            // 添加移除选中类名
            value.selected
              ? ruleCalc.addClass(value, this.$options.selectName)
              : ruleCalc.removClass(value, this.$options.selectName);
          });
        } else {
          next = this.getSign(next);
        }
      }
      ruleCalc.addClass(next, this.$options.focusName);
      next.__pools__.setPointer(next);
      if (typeof next.vFocus === "function") {
        next.vFocus(next);
      }
      return next;
    }
  }

  /**
   * 焦点移动方法
   * @param {String} direction 移动方向（left,right,up,down）
   * @param {Object} pointer 焦点对象
   */
  moveFocus(direction, pointer = this.pointer) {
    // 判断当前有焦点时再移动
    return this.checkFocus(ruleCalc.rulesInArea(direction, pointer));
  }

  /**
   * 根据位置信息设置焦点
   * @param {Number} left 元素X方向偏移量
   * @param {Number} top 元素Y方向偏移量
   * @param {Function} cb 设置焦点后需要执行的方法(非必传)
   */
  checkPosition(left, top, cb = undefined) {
    let tempArr = this[getList](this.popSign);
    let temp = ruleCalc.rulesPosition(left, top, tempArr);
    if (temp.sign) {
      temp = this.getSign(temp);
    }
    this.checkFocus(temp);
    typeof cb === "function" && cb(this.pointer);
  }

  /**
   * 自定义条件初始化焦点
   * @param {Function} fn 焦点筛选条件方法，并返回Boolean值
   * @param {Function} cb 非必传字段、初始化焦点后需要执行的方法
   */
  createFocus(fn, cb = undefined) {
    let tempArr = this[getList](this.popSign);
    let temp = ruleCalc.ergodicList(tempArr, fn);
    if (temp.sign) {
      temp = this.getSign(temp);
    }
    this.checkFocus(temp);
    typeof cb === "function" && cb(this.pointer);
  }

  /**
   * 通用焦点初始化方法
   * @param {Function/Number} a 焦点筛选条件方法/焦点X方向偏移量
   * @param {Number/Function} b 焦点Y方向偏移量/焦点初始化后的回调
   * @param {Function} c 焦点初始化后的回调
   */
  initFocus(a, b = undefined, c = undefined) {
    this[$vm].prototype.$nextTick(() => {
      if (typeof a === "function") {
        this.createFocus(a, b);
      } else if (typeof a === "number" && typeof b === "number") {
        this.checkPosition(a, b, c);
      }
    });
  }

  /**
   * 下一个焦点，仅处理当前弹层水平方向的下一个
   * @param {Boolean} flag 触及边界时是否跳到下一行第一个
   * @param {Function} fn 触及边界且换行成功后执行的方法
   */
  next(flag, fn) {
    let pointer;
    if (this.pointer) {
      pointer = ruleCalc.rulesInArea("right", this.pointer);
      if (pointer) {
        // 未到边界继续执行向右的逻辑
        this.checkFocus(pointer);
      } else if (flag) {
        pointer = ruleCalc.rulesTurnDown(this.pointer);
        pointer && this.checkFocus(pointer) && typeof fn === "function" && fn();
      }
    } else {
      pointer = this.checkPosition(0, 0);
      flag && pointer && typeof fn === "function" && fn();
    }
    return pointer;
  }

  /**
   * 上一个焦点，仅处理当前弹层水平方向的上一个
   * @param {Boolean} flag 触及边界时是否跳到上一行最后一个
   * @param {Function} fn 触及边界且换行成功后执行的方法
   */
  prev(flag, fn) {
    let prev = this.pointer;
    let next = null;
    if (prev) {
      next = ruleCalc.rulesInArea("left", prev);
      if (next) {
        // 未到边界继续执行向右的逻辑
        this.checkFocus(next);
      } else if (flag) {
        next = ruleCalc.rulesTurnUp(prev);
        next && this.checkFocus(next) && typeof fn === "function" && fn();
      }
    } else {
      next = this.checkPosition(0, 0);
      flag && next && typeof fn === "function" && fn();
    }
    return next;
  }

  /**
   * 移动到当前焦点所在行的第一个|移动标记到焦点所在行的第一个
   * @param {Boolean|Object} createrOrFlag 是否限定区域|含有sign标记的焦点对象
   * @returns Object
   */
  start(createrOrFlag) {
    if (typeof createrOrFlag === "object") {
      if (createrOrFlag.sign) {
        let next = createrOrFlag;
        let prev = createrOrFlag;
        while ((prev = ruleCalc.rulesInSign("left", prev))) {
          next = prev;
        }
        if (next !== createrOrFlag) {
          ruleCalc.removClass(createrOrFlag, this.$options.selectName);
          ruleCalc.addClass(next, this.$options.selectName);
        }
        return next;
      } else {
        console.error(`您当前选择的焦点对象，未含有sign标记`);
      }
    } else {
      if (this.pointer) {
        let target = this.pointer;
        let tempObj = null;
        // 仅在焦点标记的区域、或默认全局移动到左侧边界
        while (
          (tempObj = ruleCalc.rulesInArea("left", target, createrOrFlag))
        ) {
          target = tempObj;
        }
        // 移动到不同标记的焦点时，自动切到对应选中节点上
        if (target.sign !== this.pointer.sign) {
          target = this.getSign(target.sign, target.popup);
        }
        return this.checkFocus(target);
      } else {
        return this.checkPosition(0, 0);
      }
    }
  }

  /**
   * 移动到当前焦点所在行的最后一个|移动标记到焦点所在行的最后一个
   * @param {Boolean|Object} createrOrFlag 是否限定区域|含有sign标记的焦点对象
   * @returns Object
   */
  end(createrOrFlag) {
    if (typeof createrOrFlag === "object") {
      if (createrOrFlag.sign) {
        let next = createrOrFlag;
        let prev = createrOrFlag;
        while ((prev = ruleCalc.rulesInSign("right", prev))) {
          next = prev;
        }
        if (next !== createrOrFlag) {
          ruleCalc.removClass(createrOrFlag, this.$options.selectName);
          ruleCalc.addClass(next, this.$options.selectName);
        }
        return next;
      } else {
        console.error(`您当前选择的焦点对象，未含有sign标记`);
      }
    } else {
      if (this.pointer) {
        let target = this.pointer;
        let tempObj = null;
        // 仅在焦点标记的区域、或默认全局移动到左侧边界
        while (
          (tempObj = ruleCalc.rulesInArea("right", target, createrOrFlag))
        ) {
          target = tempObj;
        }
        // 移动到不同标记的焦点时，自动切到对应选中节点上
        if (target.sign !== this.pointer.sign) {
          target = this.getSign(target.sign, target.popup);
        }
        return this.checkFocus(target);
      } else {
        return this.checkPosition(0, 0);
      }
    }
  }

  /**
   * 清除当前层级焦点内容
   */
  clearFocus() {
    let popup = this.popSign;
    this[ergodicViews]((value) => {
      let layer = value.getLayer(popup);
      if (layer) {
        let { pointer } = layer;
        if (pointer) {
          ruleCalc.removClass(pointer, this.$options.focusName);
          typeof this.pointer.vBlur === "function" && pointer.vBlur(pointer);
        }
        layer.setPointer();
      }
    });
  }

  /**
   * 处理同一标记类型，当前选中状态
   * @param {String} sign 元素标记名称
   * @param {Function} fn 返回布尔类型来标记当前焦点选中或未选中
   * @param {String} popup 节点所在层级标记（非必传）
   */
  checkSign(sign, fn, popup = "") {
    let tempArr = this.getSignList(sign, popup);
    tempArr.forEach((value) => {
      value.selected = fn(value);
      // 添加移除选中类名
      value.selected
        ? ruleCalc.addClass(value, this.$options.selectName)
        : ruleCalc.removClass(value, this.$options.selectName);
    });
  }

  /**
   * 获取指定标记类型选中状态的数据内容
   * @param {String|Object} createrOrSign 元素标记名称|焦点元素
   * @param {String} popup 节点所在层级标记（非必传）
   */
  getSign(createrOrSign, popup = "") {
    let data = null;
    if (typeof createrOrSign === "object") {
      let { __sign__ } = createrOrSign;
      __sign__.forEach((val) => {
        val.selected && (data = val);
      });
      if (!data) {
        ruleCalc.addClass(createrOrSign, this.$options.selectName);
        createrOrSign.selected = true;
        data = createrOrSign;
      }
    } else if (typeof createrOrSign === "string") {
      let tempArr = this.getSignList(createrOrSign, popup);
      tempArr.forEach((value) => {
        value.selected && (data = value);
      });
      !data &&
        console.error(`您查看的标记为${createrOrSign}的列表中，没有已选中元素`);
    }
    return data;
  }

  /**
   * 下一个标记移动方法
   * @param {String} sign 标记名称
   * @param {String} direction 方向（默认为向右移动）
   * @param {String} popup 弹窗标记，非必传
   */
  nextSign(sign, direction = "right", popup) {
    let prev = this.getSign(sign, popup);
    let next = ruleCalc.rulesInSign(direction, prev);
    if (next) {
      ruleCalc.removClass(prev, this.$options.selectName);
      ruleCalc.addClass(next, this.$options.selectName);
    }
    return next;
  }

  /**
   * 上一个标记移动方法
   * @param {String} sign 标记名称
   * @param {String} direction 方向（默认为向左移动）
   * @param {String} popup 弹窗标记，非必传
   */
  prevSign(sign, direction = "left", popup) {
    let prev = this.getSign(sign, popup);
    let next = ruleCalc.rulesInSign(direction, prev);
    if (next) {
      ruleCalc.removClass(prev, this.$options.selectName);
      ruleCalc.addClass(next, this.$options.selectName);
    }
    return next;
  }

  /**
   * 获取指定弹层焦点
   * @param {String} popup 弹层标记名称
   * @returns pointer
   */
  getPointer(popup) {
    let prePointer = null;
    this[ergodicViews]((value) => {
      let layer = value.getLayer(popup);
      return (prePointer = layer ? layer.pointer : null);
    });
    return prePointer;
  }

  /**
   * 根据路由创建视图层，并变更当前路径对象
   * @param {VUE Router} to VUE路由守卫中的to
   */
  createView(to) {
    // 未匹配到路由信息，不创建视图层
    if (!to.matched.length) {
      return false;
    }
    let parent = this[$views];
    let tempPath = [];
    // 根据匹配到的路由信息创建视图层
    to.matched.forEach((value) => {
      if (value.name) {
        if (
          !hasOwn(parent.routeViews, value.name) ||
          !(parent.routeViews[value.name] instanceof Views)
        ) {
          parent.routeViews[value.name] = new Views(parent);
        }
        parent.viewSign = value.name;
        parent = parent.routeViews[value.name];
        tempPath.push(value.name);
      } else {
        throw new Error(`路由：${value.path}，未指定name属性`);
      }
    });
    this[path] = tempPath;
  }

  /**
   * 按当前路径遍历Views对象
   * @param {Function} func 回调函数，接收匹配当前路由的view对象
   */
  [ergodicViews](func) {
    let views = null;
    for (let i = -1; i < this[path].length; i++) {
      views = i < 0 ? this[$views] : views.routeViews[this[path][i]];
      if (func(views)) {
        break;
      }
    }
  }

  /**
   * 获取焦点所属Views
   * @param {Number/String} routeDepath 节点路有深度
   * @returns 焦点所属焦点池
   */
  [getView](routeDepath, routeVpath) {
    if (routeDepath === "public") {
      return this[$views];
    } else {
      let list = routeVpath ? routeVpath.split(":::") : this[path];
      let length = routeVpath ? list.length - 1 : routeDepath - 0;
      let views = this[$views];
      for (let i = 0; i <= length; i++) {
        views = views.routeViews[list[i]];
      }
      return views;
    }
  }

  /**
   * 获取指定popup弹层基础焦点列表
   * @param {String} popup 焦点对象或弹层标记
   * @returns Array
   */
  [getList](createrOrPopup) {
    if (typeof createrOrPopup === "object") {
      let { __list__ } = createrOrPopup;
      return __list__;
    } else if (!createrOrPopup || typeof createrOrPopup === "string") {
      let list = null;
      // 获取指定弹层或当前页面焦点列表
      this[ergodicViews]((value) => {
        let layer = value.getLayer(createrOrPopup);
        if (layer) {
          if (createrOrPopup) {
            return (list = layer.items);
          } else {
            let { viewSign, routeViews } = value;
            let child =
              viewSign && routeViews[viewSign]
                ? [routeViews[viewSign].__item__]
                : [];
            return (list = layer.items.concat(child));
          }
        }
      });
      return list || [];
    }
  }

  /**
   * 根据弹层和标签标记名称获取标记列表
   * @param {String} createrOrSign 分组标记名称
   * @param {String} popup 弹层标记名称
   * @returns Array
   */
  getSignList(createrOrSign, popup) {
    let list = [];
    if (typeof createrOrSign === "object") {
      list = createrOrSign.__sign__;
    } else if (typeof createrOrSign === "string") {
      let attrStr = popup || "rootPopup";
      this[ergodicViews]((val) => {
        if (hasOwn(val, attrStr)) {
          let temp = val[attrStr].getSignList(createrOrSign);
          if (temp) {
            return (list = temp);
          }
        }
      });
    }
    return list;
  }

  /**
   * 注册节点
   * @param {Creater} creater 节点对象
   */
  register(creater) {
    let { appoints, name, routeDepath, routerEl, routeVpath, sign } = creater;
    let views = this[getView](routeDepath, routeVpath);
    // 添加views指向
    creater.__views__ = views;
    views.registeLayer(creater);
    if (!views.__item__) {
      views.registeEl(new BaseView(routerEl, views));
    }
    if (name === "items") {
      // 添加默认选中状态类名
      sign &&
        creater.selected &&
        ruleCalc.addClass(creater, this.$options.selectName);
      // 添加默认焦点
      if (appoints) {
        creater.__pools__.setPointer(creater);
        ruleCalc.addClass(creater, this.$options.focusName);
      }
    }
  }

  /**
   * 更新焦点/区域
   * @param {HTMLElement|Element} elem html元素节点
   * @param {Object} binding 含自定义指令绑定信息的对象
   * @param {Vnode} vnode 虚拟节点
   */
  updater(elem, binding, vnode) {
    let creater = elem.__$Focus__;
    creater.update(elem, binding, vnode);
  }

  /**
   * 移除节点
   * @param {DocumentElement} elem 元素节点
   * @param {Boolean} flag 区分item、group的删除；真：item的删除，假：group的删除
   */
  remover(elem) {
    let creater = elem.__$Focus__;
    creater.__pools__.splicePool(creater);
    // 清空所有引用
    if (!creater.__pools__.items.length) {
      // 解绑当前pools
      if (creater.popup) {
        delete creater.__views__[`popup_${creater.popup}`];
      }
      let tempFlag = false;
      for (let [key, val] of Object.entries(creater.__views__)) {
        if (/^(rootPopup|popup_.+)$/.test(key)) {
          tempFlag = tempFlag || !!val.items.length ? true : false;
        }
      }
      if (!tempFlag) {
        // 解绑当前view
        let vPath = creater.routeVpath.split(":::");
        let currentPath = this[path].toString();
        let reg = new RegExp("^" + vPath.toString());
        if (!reg.test(currentPath)) {
          let routeViews = this[$views].routeViews;
          let i = 0;
          while (i < vPath.length - 1) {
            routeViews = routeViews[vPath[i++]].routeViews;
          }
          delete routeViews[vPath[vPath.length - 1]];
        }
      }
    }
    creater.__views__ = null;
    creater.__pools__ = null;
    creater.__group__ = null;
    elem.__$Focus__ = null;
  }
}
